package cz.worktracker.controller;

import cz.worktracker.enums.DataSetsEnum;
import cz.worktracker.model.entities.Customer;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Map;
import java.util.logging.Level;
import javax.annotation.ManagedBean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;
import javax.inject.Inject;
import javax.inject.Named;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Transaction;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

/**
 * CustomerManager
 * Controller pro práci s JPA entitou Customer.
 * @since 2010-04-22
 * @author kyjovsky, pagac
 */
@ManagedBean
@SessionScoped
@Named
public class CustomerManager extends AbstractManager
                                implements Serializable {

    private static final long SerialVersionUID = 1L;

    private static final String MAIN_ORDERING = "name";

    private static final String NAV_LIST = "list";
    private static final String NAV_EDIT = "edit";
    private static final String NAV_DETAIL = "detail";
    private static final String NAV_REDIRECT = "?faces-redirect=true";
    
    private Customer customer;
    private AbstractPaginator paginator;
    private DataModel dataModel;
    private DataSetsEnum DSEnum;
    private ArrayList selectList;
    private int rowsPerPage = DSEnum.ITEMS_PER_PAGE_DEFAULT.getValue();

    private String searchPattern = "";

    private String sortByField = DSEnum.SORT_BY_FIELD.getStringValue();
    private String sortDirection = DSEnum.SORT_DIRECTION.getStringValue();

    @Inject UserManager userManager;
    @Inject ProjectManager projectManager;

    
    /**
     * Constructor
     * Musí být prázdný u non-Session scoped beanu.
     */
    public CustomerManager() { }

    @PostConstruct
    public void construct() {
        init();
    }

    @PreDestroy
    public void destroy() {
        customer = null;
        paginator = null;
        dataModel = null;
    }


    /**
     * Inicializace při vytvoření beanu.
     */
    public void init() {
        if( customer == null ) {
            customer = new Customer();
            customer.setIdUser(-1);
            dataModel = null;
            paginator = null;
        }
    }

    public boolean getNewCustomer() {
        customer = null;
        init();
        return true;
    }

    /**
     * Vytvoří zaznam zákazníka a uloží (Customer) do databáze.
     * @return
     */
    public String create() {
        if (customer != null) {
            try {
                customer.setIsActive("y");
                customer.setIdUser(userManager.getUser().getId());
                if(customer.getPricePerRateUnit() == null) customer.setPricePerRateUnit(new Float (0.0));
                Transaction tx = getSession().beginTransaction();
                getSession().save(customer);
                tx.commit();
                customer = null;
                init();
            } catch (Exception e) {
                getLogger(getClass()).log(Level.SEVERE, "Error on try to save Customer: " + getCustomer(), e);
                addMessage("Error on try to save Customer", FacesMessage.SEVERITY_ERROR);
                return null;
            }
        }
        return NAV_LIST + NAV_REDIRECT;
    }


    /**
     * Updates customer record in database.
     * @return String
     */
    public String update() {
        if (customer != null) {
            try {
                if(customer.getPricePerRateUnit() == null) customer.setPricePerRateUnit(new Float (0.0));
                Transaction tx = getSession().beginTransaction();
                getSession().update(customer);
                tx.commit();
                customer = null;
                init();
            } catch (Exception e) {
                getLogger(getClass()).log(Level.SEVERE, "Error on try to save Customer: " + getCustomer(), e);
                addMessage("Error on try to save Customer", FacesMessage.SEVERITY_ERROR);
                return null;
            }
        }

        return NAV_LIST + NAV_REDIRECT;
    }


    /**
     * Method sets current user record for editation.
     * @return String
     */
    public String edit() {
        customer = (Customer) getList().getRowData();

        return NAV_EDIT + NAV_REDIRECT;
    }


    /**
     * Method removes (inactivate) customer record.
     * @return String
     */
    public void remove() {
        customer = (Customer) getList().getRowData();

        Transaction tx = getSession().beginTransaction();
        customer.setIsActive( "n" );
        getSession().update( customer );
        tx.commit();
        customer = null;

        getCustomersCount();

        resetPagination();
        resetDataModel();

        // return NAV_LIST + NAV_REDIRECT;
    }


    /**
     * Redirects to detail info page.
     * @return
     */
    public String detail() {
        customer = (Customer) getList().getRowData();
        return NAV_DETAIL + NAV_REDIRECT;
    }


    /**
     * Prepares dataModel.
     * @return Hell I really want to know.
     */
    public String prepareDataModel() {
        resetDataModel();
        return NAV_LIST + NAV_REDIRECT;
    }
    

    /**
     * Method nulls reference of dataModel.
     */
    private void resetDataModel() {
        dataModel = null;
    }


    /**
     * Method nulls reference of paginator.
     */
    public void resetPagination() {
        paginator = null;
    }


    /**
     * Returns page switcher and override abstract methods from AbstractPaginator.
     * @return Paginator object.
     */
    public AbstractPaginator getPaginator() {
        if( paginator == null ) {
            paginator = new AbstractPaginator( rowsPerPage ) {

               /*
                * While creating a new instance we have to implement abstract
                * methods defined in AbstractPaginator class.
                */

                @Override
                public int getItemsCount() {
                    return getCustomersCount();
                }

                @Override
                public DataModel createPageDataModel() {
                    ListDataModel list = null;

                    try {
                        Transaction tx = getSession().beginTransaction();
                        Criteria ct = getSession().createCriteria(Customer.class);
                        ct.add( Restrictions.eq( "idUser", userManager.getUser().getId() ) );
                        ct.add( Restrictions.eq( "isActive", "y" ) );

                        if( getSearchPattern().length() > 0 ) {
                            String pattern = "%" + getSearchPattern() + "%";
                            ct.add( Restrictions.disjunction()
                                  .add( Restrictions.ilike("name", pattern) )
                                  .add( Restrictions.ilike("note", pattern) )
                                  .add( Restrictions.ilike("address", pattern) )
                                  .add( Restrictions.ilike("city", pattern) )
                                  .add( Restrictions.ilike("dic", pattern) )
                                  .add( Restrictions.ilike("ic", pattern) )
                                  .add( Restrictions.ilike("email", pattern) )
                                  .add( Restrictions.ilike("phone", pattern) )
                                  .add( Restrictions.ilike("zipCode", pattern) )
                              );
                        }
                        
                        if( getSortDirection().equalsIgnoreCase( "asc" ) ) {
                            ct.addOrder( Order.asc( getSortByField()) );
                        } else {
                            ct.addOrder( Order.desc( getSortByField()) );
                        }

                        ct.setFirstResult(getPageFirstItem());
                        ct.setMaxResults(getPageSize());
                        list = new ListDataModel(ct.list());
                        tx.commit();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    return list;
                }
            };
        }

        return paginator;
    }


    /**
     * Returns count of records returned to view.
     * @return int
     */
    public int getRowsPerPage() {
        return rowsPerPage;
    }


    /**
     * Sets count of records returned to view.
     * @param rowsPerPage
     */
    public void setRowsPerPage( int rowsPerPage ) {
        this.rowsPerPage = rowsPerPage;

        resetDataModel();
        resetPagination();
    }


    /**
     * Returns DataModel for dataTable.
     * @return DataModel for dataTable.
     */
    public DataModel getList() {
        if( dataModel == null ) {
            dataModel = getPaginator().createPageDataModel();
        }

        return dataModel;
    }



    public ArrayList getSelectList() {
        if( selectList == null ) {
            selectList = createSelectList();
        }

        return selectList;
    }

    public ArrayList createSelectList() {
        ArrayList list = null;
        try {
            Transaction tx = getSession().beginTransaction();
            String hql = "from Customer as customer where customer.idUser = '"+ userManager.getUser().getId() +"'" +
                            " and customer.isActive = 'y'";
            Query query = getSession().createQuery(hql);

            list = new ArrayList();
            Customer tempCustomer;

            for (int i = 0; i < query.list().size(); i++) {
                tempCustomer = (Customer) query.list().get(i);
                list.add(new SelectItem(tempCustomer.getId(), tempCustomer.getName()));
                tempCustomer = null;
            }
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return list;
    }

    /**
     * Next page.
     */
    public void next() {        
        getPaginator().nextPage();
        resetDataModel();
    }

    /**
     * Last page.
     */
    public void last() {
        getPaginator().lastPage();
        resetDataModel();
    }


    /**
     * Previous page.
     */
    public void previous() {
        getPaginator().previousPage();
        resetDataModel();
    }

    /**
     * First page.
     */
    public void first() {
        getPaginator().firstPage();
        resetDataModel();
    }


    /**
     * Increments count of items per page.
     */
    public void more() {
        setRowsPerPage(getRowsPerPage() + DSEnum.ITEMS_PER_PAGE_INCREMENT.getValue());

        resetPagination();
        resetDataModel();
    }


    /**
     * * Decrements count of items per page.
     */
    public void less() {
        setRowsPerPage(getRowsPerPage() - DSEnum.ITEMS_PER_PAGE_INCREMENT.getValue());

        resetPagination();
        resetDataModel();
    }


    /**
     * Retrieves sort params through context and initiates sort.
     */
    public void sort() {
        /**
         * Try to gain params send by <f:param />
         */
        FacesContext context = FacesContext.getCurrentInstance();
        Map requestMap = context.getExternalContext().getRequestParameterMap();
        setSortByField( (String) requestMap.get("by") );
        setSortDirection( (String) requestMap.get("order") );

        resetPagination();
        resetDataModel();
        
    }


    /**
     * Initiates searching.
     */
    public void search() {
        /**
         * Try to gain params send by <f:param />
         */
        //FacesContext context = FacesContext.getCurrentInstance();
        //Map requestMap = context.getExternalContext().getRequestParameterMap();
        //setSortByField( (String) requestMap.get("by") );

        resetPagination();
        resetDataModel();
    }


    /**
     * Cancels searching.
     */
    public void cancelSearch() {
        setSearchPattern( "" );

        resetPagination();
        resetDataModel();
    }


    /**
     * Returns customers count.
     * @return int means customers count.
     */
    public int getCustomersCount() {
        Transaction tx = getSession().beginTransaction();
        String hql = "select count(*) from Customer as customer where customer.idUser = '"
                + userManager.getUser().getId() + "' and customer.isActive = 'y'";
        int num =  ( (Long) (getSession().createQuery(hql).iterate().next()) ).intValue();
        tx.commit();
        return num;
    }


    /**
     * Returns count of records in Data Model.
     * @return int
     */
    public int getDataModelRecordsCount() {

        if( getList() != null ) {
            return getList().getRowCount();
        }

        return 0;
    }


    /**
     * Returns customer.
     * @return Customer.
     */
    public Customer getCustomer() {
        return customer;
    }


    /**
     * Set customer.
     * @param customer
     */
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }


    /**
     * Returns field name in JPA entity according which sorting will be provided.
     * @return String
     */
    public String getSortByField() {
        return sortByField;
    }


    /**
     * Sets field name in JPA entity according which sorting will be provided.
     * @param sortByField
     */
    public void setSortByField(String sortByField) {
        this.sortByField = sortByField;
    }

    
    /**
     * Returns sort direction (ascending: asc; descending desc).
     * @return String
     */
    public String getSortDirection() {
        return sortDirection;
    }


    /**
     * Sets sort direction (ascending: asc; descending desc).
     * @param sortDirection
     */
    public void setSortDirection(String sortDirection) {
        this.sortDirection = sortDirection;
    }


    /**
     * Returns searched string.
     * @return String
     */
    public String getSearchPattern() {
        return searchPattern;
    }


    /**
     * Sets searched string.
     * @param searchPattern
     */
    public void setSearchPattern(String searchPattern) {
        this.searchPattern = searchPattern;
    }


    /**
     * Creates new project with
     * @return
     */
    public String createNewProject() {
        Customer tmpCustomer = (Customer) dataModel.getRowData();

        projectManager.setNullFlag( false );
        projectManager.getProject().setIdCustomer( tmpCustomer.getId() );
        projectManager.getProject().setRateType( tmpCustomer.getRateType() );
        projectManager.getProject().setPricePerRateUnit( tmpCustomer.getPricePerRateUnit() );

        tmpCustomer = null;

        return "/user/project/create" + NAV_REDIRECT;
    }

     public String showList() {
        resetDataModel();
        return "/user/customer/list" + NAV_REDIRECT;
     }


}
